
//  Copyright (C) 2011 Luca Piccioni
// 
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
// 
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
// 
//  You should have received a copy of the GNU General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.

using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;

namespace Derm
{
	/// <summary>
	/// 
	/// </summary>
	/// <typeparam name="T"></typeparam>
	public class PluginLoader<T> where T : class, IPlugin
	{
		/// <summary>
		/// Register a plugin.
		/// </summary>
		/// <param name="plug">
		/// A generic type collected as available plugin.
		/// </param>
		public void RegisterPlugin(T plug)
		{
			if (plug.CheckAvailability() == false)
				throw new ArgumentException("not available", "plug");
			mPlugins.Add(plug);
		}

		/// <summary>
		/// Directly get a registered plugin instance by its name.
		/// </summary>
		/// <param name="name"></param>
		/// <returns></returns>
		public T GetPlugin(string name)
		{
			return (mPlugins.FindLast(delegate(T plugin) { return (plugin.Name == name); }));
		}

		/// <summary>
		/// Load all compatible plugins from a directory.
		/// </summary>
		/// <param name="pPath">
		/// A <see cref="System.String"/> that specifies the directory path.
		/// </param>
		/// <param name="pluginFactoryType">
		/// A <see cref="System.String"/> that specify the full name of the type that create the plugin. This type
		/// shall have a method named CreatePlugin which returns a <typeparamref name="T"/>.
		/// </param>
		/// <exception cref="ArgumentNullException">
		/// This exception is thrown if the parameter <paramref name="pPath"/> is null.
		/// </exception>
		/// <exception cref="ArgumentException">
		/// This exception is thrown if the parameter <paramref name="pPath"/> doesn't specify an existing directory.
		/// </exception>
		protected void LoadImageCodecPlugins(string pPath, string pluginFactoryType)
		{
			if (pPath == null)
				throw new ArgumentNullException("pPath");
			if (Directory.Exists(pPath) == false)
				throw new ArgumentException("path not existing", "pPath");

			string[] plugPaths = Directory.GetFiles(pPath, "*.dll", SearchOption.TopDirectoryOnly);

			foreach (string plugPath in plugPaths) {
				try {
					T plugin = LoadManagedPlugin(plugPath, pluginFactoryType);

					if (plugin != null)
						mPlugins.Add(plugin);
					continue;
				} catch (Exception e) {
					// XXX
				}
			}
		}

		/// <summary>
		/// Really loads a plugin type implementation from a dynamically loaded library.
		/// </summary>
		/// <param name="pPath">
		/// A <see cref="System.String"/> that specifies the path of the dynamically loaded library.
		/// </param>
		/// <param name="pluginFactoryType">
		/// A <see cref="System.String"/> that specify the full name of the type that create the plugin. This type
		/// shall have a method named CreatePlugin which returns a <typeparamref name="T"/>.
		/// </param>
		/// <exception cref="ArgumentNullException">
		/// This exception is thrown if the parameter <paramref name="pPath"/> is null.
		/// </exception>
		/// <exception cref="FileNotFoundException">
		/// This exception is thrown if the file <paramref name="pPath"/> cannot be found.
		/// </exception>
		/// <exception cref="FileLoadException">
		/// This exception is thrown if the file <paramref name="pPath"/> cannot be loaded.
		/// </exception>
		/// <exception cref="BadImageFormatException">
		/// This exception is thrown if the file <paramref name="pPath"/> is not a valid assembly.
		/// </exception>
		protected static T LoadManagedPlugin(string pPath, string pluginFactoryType)
		{
			Assembly hLibrary = Assembly.LoadFile(pPath);
			Type plugImageCodecFactoryType = hLibrary.GetType(pluginFactoryType, true, false);
			object plugImageCodecFactory = Activator.CreateInstance(plugImageCodecFactoryType);
			
			MethodInfo plugFactoryCreate = plugImageCodecFactoryType.GetMethod("CreatePlugin", BindingFlags.Instance|BindingFlags.Public);

			if (plugFactoryCreate.ReturnType != typeof(T))
				return (default(T));

			object plugInstance = plugFactoryCreate.Invoke(plugImageCodecFactory, null);

			return ((T) plugInstance);
		}

		/// <summary>
		/// Loaded and available plugins.
		/// </summary>
		protected IEnumerable<T> Plugins { get { return (mPlugins); } }

		/// <summary>
		/// Loaded plugins.
		/// </summary>
		private readonly List<T> mPlugins = new List<T>();
	}
}
